package com.atguigu.juc;

import java.util.concurrent.TimeUnit;

/**
 * @author tongatong
 * @name SynchronizedTest
 * @description synchronized 的对象锁和类锁
 * 问题：
 * 1 有两个线程标准访问，请问先打印邮件还是短信？
 * 2 邮件方法暂停2秒钟，请问先打印邮件还是短信？
 * 3 新增一个hello普通方法，请问先打印邮件还是hello？
 * 4 有两部手机，请问先打印邮件还是短信？
 * 5 有两个静态同步方法，同一部手机，请问先打印邮件还是短信？
 * 6 有两个静态同步方法，有2部手机，请问先打印邮件还是短信？
 * 7 有1个静态同步方法，有1个普通同步方法,有1部手机，请问先打印邮件还是短信？
 * 8 有1个静态同步方法，有1个普通同步方法,有2部手机，请问先打印邮件还是短信？
 *
 * 答案：
 * （1）邮件。（2）邮件。（3）hello。（4）短信。
 * （5）邮件。（6）邮件。（7）短信。（8）短信。
 *
 * 实现：
 *  1. 手机不同，用对象不同表示
 *
 * 结论：
 *  1. 作用在普通同步方法上的synchronized，被称为对象锁，锁的是当前对象。
 *  2. 作用在静态同步方法上的synchronized，被称作类锁，锁的是模板类。
 *  3. 对象锁和类锁是两个不同的锁，因此，彼此之间不构成竞争关系。
 *     即，即使当前模板类被锁后，该类的普通同步方法，仍可以被访问到。
 *  4. 同步代码块：
 *       锁对象是 对象 的话，是对象锁，
 *       锁对象是 XXX.class 的话，是类锁。
 *
 *   synchronized
 *   作用在方法上：对当前对象this添加锁，该对象中定义的所有synchronize方法都被加上锁，称为对象锁。
 *   作用在静态方法上：对当前类 xxx.class 添加锁，这个类模板都都不能被其他线程访问，称为类锁。
 *
 * @date 2021/8/31
 * @time 20:37
 */
public class SynchronizedTest {
    public static void main(String[] args) throws InterruptedException {
        Phone phone1 = new Phone();
        Phone phone2 = new Phone();

//        Thread t1 = new Thread(phone1::sendEmail, "t1");
        Thread t1 = new Thread(Phone::sendEmail, "t1");
        Thread t2 = new Thread(phone1::sendSMS, "t2");
//        Thread t2 = new Thread(Phone::sendSMS, "t2");
//        Thread t2 = new Thread(phone2::sendSMS, "t2");
//        Thread t2 = new Thread(phone1::hello, "t2");

        t1.start();
        //这里只是为了保证t1和t2的执行顺序
        TimeUnit.MICROSECONDS.sleep(10);
        t2.start();
    }
}

class Phone {
    /*public synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(2);

            System.out.println("---email---");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }*/
    public static synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(2);

            System.out.println("---email---");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public synchronized void sendSMS() {
        System.out.println("---SMS---");
    }
    /*public static synchronized void sendSMS() {
        System.out.println("---SMS---");
    }*/
    public void hello() {
        System.out.println("hello");
    }
}



//教师笔记
/**
 * @auther zzyy
 * @create 2020-03-26 11:41
 * 题目：谈谈你对多线程锁的理解
 * 线程   操作  资源类  8锁案例
 *
 * 1 有两个线程标准访问，请问先打印邮件还是短信？
 * 2 邮件方法暂停4秒钟，请问先打印邮件还是短信？
 * 3 新增一个hello普通方法，请问先打印邮件还是hello？
 * 4 有两部手机，请问先打印邮件还是短信？
 * 5 有两个静态同步方法，同一部手机，请问先打印邮件还是短信？
 * 6 有两个静态同步方法，有2部手机，请问先打印邮件还是短信？
 * 7 有1个静态同步方法，有1个普通同步方法,有1部手机，请问先打印邮件还是短信？
 * 8 有1个静态同步方法，有1个普通同步方法,有2部手机，请问先打印邮件还是短信？
 *
 *  *  1-2
 *  *  *  一个对象里面如果有多个synchronized方法，某一个时刻内，只要一个线程去调用其中的一个synchronized方法了，
 *  *  *  其它的线程都只能等待，换句话说，某一个时刻内，只能有唯一一个线程去访问这些synchronized方法
 *  *  *  锁的是当前对象this，被锁定后，其它的线程都不能进入到当前对象的其它的synchronized方法
 *
 *  3-4
 *   *  加个普通方法后发现和同步锁无关
 *  *  换成两个对象后，不是同一把锁了，情况立刻变化。
 *  5-6
 *  都换成静态同步方法后，情况又变化
 *  若是普通同步方法，new     this,   具体的一部部手机,所有的普通同步方法用的都是同一把锁——实例对象本身，
 *  若是静态同步方法，static  Class ，唯一的一个模板 *
 *
 *  7-8
 *   *  当一个线程试图访问同步代码时它首先必须得到锁，退出或抛出异常时必须释放锁。
 *  *
 *  *  所有的普通同步方法用的都是同一把锁——实例对象本身，，就是new出来的具体实例对象本身
 *  *  也就是说如果一个实例对象的普通同步方法获取锁后，该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能获取锁，
 *  *  可是别的实例对象的普通同步方法因为跟该实例对象的普通同步方法用的是不同的锁，所以不用等待该实例对象已获取锁的普通
 *  *  同步方法释放锁就可以获取他们自己的锁。
 *  *
 *  *  所有的静态同步方法用的也是同一把锁——类对象本身，就是我们说过的唯一模板Class
 *  *  具体实例对象this和唯一模板Class，这两把锁是两个不同的对象，所以静态同步方法与普通同步方法之间是不会有竞态条件的。
 *  *  但是一旦一个静态同步方法获取锁后，其他的静态同步方法都必须等待该方法释放锁后才能获取锁 *
 *
 *   *
 *  *  synchronized是实现同步的基础：Java中的每一个对象都可以作为锁。
 *  *  具体表现为以下3种形式。
 *     作用于实例方法，当前实例加锁，进入同步代码前要获得当前实例的锁；
 *     作用于静态方法，当前类加锁，进去同步代码前要获得当前类对象的锁；
 *     作用于代码块，对括号里配置的对象加锁。
 */
